package logger

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

//#region 轮转配置

// RotateMode 定义轮转模式类型
type RotateMode string

// 预定义的轮转模式
const (
	RotateYearly  RotateMode = "yearly"  // 按年轮转
	RotateMonthly RotateMode = "monthly" // 按月轮转
	RotateWeekly  RotateMode = "weekly"  // 按周轮转
	RotateDaily   RotateMode = "daily"   // 按天轮转, 默认
	RotateHourly  RotateMode = "hourly"  // 按小时轮转
)

// RotateConfig 日志轮转配置
type RotateConfig struct {
	Enabled    bool       // 是否启用轮转
	Mode       RotateMode // 轮转模式
	MaxAge     int        // 日志保留天数，0表示永久保留
	lastRotate time.Time  // 上次轮转时间
}

// DefaultRotateConfig 默认轮转配置:
// - 启用日志轮转
// - 按天轮转, 默认
// - 保留30天
var DefaultRotateConfig = RotateConfig{
	Enabled: true,        // 启用日志轮转
	Mode:    RotateDaily, // 按天轮转
	MaxAge:  30,          // 保留30天
}

//#endregion END

// FileWriter 文件日志写入器
type FileWriter struct {
	mu            sync.Mutex      // 互斥锁
	logger        *log.Logger     // 标准日志记录器
	contentParser *TemplateParser // 模板解析器
	fileParser    *TemplateParser // 文件名解析器

	// 基础配置, Caller 传输过来
	path           string        // 日志文件路径
	outputTemplate string        // 输出模板，支持变量替换, 默认使用 DefaultOutputTemplate="app_{yyyyMMdd}.log"
	rotate         *RotateConfig // 轮转配置

	// Logger 属性
	baseFilePath string // 日志文件目录路径
	baseFileName string // 基础文件名
	fileSize     int64  // 当前文件大小

	// 输出属性
	outputFileHle  *os.File // 文件句柄
	outputFileName string   // 当前日志文件名

	// 异步写入相关
	msgChan    chan *logMessage // 日志消息通道
	done       chan struct{}    // 关闭信号
	bufferSize int              // 缓冲区大小
	async      bool             // 是否启用异步写入
	closed     int32            // 是否已关闭，用于原子操作
}

// logMessage 日志消息结构
type logMessage struct {
	level  LogLevel
	caller string
	msg    string
}

// NewFileWriter 创建文件日志写入器
func NewFileWriter(path string, outputTemplate string, rotate *RotateConfig, bufferSize int, async bool) (*FileWriter, error) {
	path = filepath.Clean(path)

	baseFilePath, baseFileName := filepath.Split(path)
	contentParser := NewTemplateParser(outputTemplate)
	fileParser := NewTemplateParser(baseFileName)

	if bufferSize <= 0 {
		bufferSize = 1000 // 默认缓冲区大小
	}

	if rotate == nil {
		rotate = &DefaultRotateConfig
	}

	w := &FileWriter{
		logger:         nil,
		path:           path,
		outputTemplate: outputTemplate,
		rotate:         rotate,
		contentParser:  contentParser,
		fileParser:     fileParser,
		baseFilePath:   baseFilePath,
		baseFileName:   baseFileName,
		bufferSize:     bufferSize,
		async:          async,
	}

	// 确保目录存在
	if err := os.MkdirAll(baseFilePath, 0755); err != nil {
		return nil, fmt.Errorf("创建日志目录失败: %w", err)
	}

	if async {
		w.msgChan = make(chan *logMessage, bufferSize)
		w.done = make(chan struct{})
		go w.asyncWriter() // 启动异步写入协程
	}

	// 初始化写入器
	if err := w.Open(); err != nil {
		return nil, fmt.Errorf("初始化写入器失败: %w", err)
	}

	return w, nil
}

// Write 实现 LogWriter 接口的文件写入方法
func (w *FileWriter) Write(level LogLevel, caller string, msg string) error {
	if w.async {
		select {
		case w.msgChan <- &logMessage{level: level, caller: caller, msg: msg}:
			return nil
		default:
			return fmt.Errorf("日志缓冲区已满")
		}
	}
	return w.writeSync(level, caller, msg)
}

// writeSync 同步写入日志
func (w *FileWriter) writeSync(level LogLevel, caller string, msg string) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	now := time.Now()

	// 重置解析器属性
	w.contentParser.Reset()

	// 设置日志内容的属性
	w.contentParser.WithProperty("Level", level)
	w.contentParser.WithProperty("Timestamp", now)
	w.contentParser.WithProperty("Message", msg)
	w.contentParser.WithProperty("Caller", caller)
	w.contentParser.WithProperty("Pid", os.Getpid())
	w.contentParser.WithProperty("Goroutine", getGoroutineID())

	content := w.contentParser.Parse()

	// 检查是否需要轮转
	if w.rotate.Enabled && w.checkTimeRotation(now) {
		if w.outputFileHle != nil {
			w.outputFileHle.Close()
			w.outputFileHle = nil
		}
		w.ensureOutputFileName(now)
		w.rotate.lastRotate = now
	}

	// 确保文件存在并打开
	if w.outputFileHle == nil {
		if err := w.initNewFile(); err != nil {
			return err
		}
	}

	// 写入日志内容
	if _, err := w.outputFileHle.WriteString(content); err != nil {
		return fmt.Errorf("写入日志失败: %w", err)
	}
	w.fileSize += int64(len(content))

	return nil
}

// asyncWriter 异步写入协程
func (w *FileWriter) asyncWriter() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintf(os.Stderr, "异步写入器发生panic: %v\n", r)
		}
	}()

	for {
		select {
		case msg, ok := <-w.msgChan:
			if !ok {
				// 通道关闭，处理完剩余消息后退出
				return
			}
			if msg == nil {
				continue
			}
			// 写入失败时重试3次
			for i := 0; i < 3; i++ {
				if err := w.writeSync(msg.level, msg.caller, msg.msg); err != nil {
					if i == 2 { // 最后一次重试失败才报错
						fmt.Fprintf(os.Stderr, "异步写入日志失败: %v\n", err)
					}
					time.Sleep(time.Millisecond * 10) // 重试前短暂等待
				} else {
					break // 写入成功就退出重试
				}
			}
		case <-w.done:
			// 收到关闭信号，但要继续处理通道中的剩余消息
			for msg := range w.msgChan {
				if msg != nil {
					_ = w.writeSync(msg.level, msg.caller, msg.msg)
				}
			}
			return
		}
	}
}

// Open 实现 LogWriter 接口
func (w *FileWriter) Open() error {
	return w.ensureOpenFile()
}

// Close 实现 LogWriter 接口
func (w *FileWriter) Close() error {
	if w.async {
		// 使用原子操作确保只关闭一次
		if !atomic.CompareAndSwapInt32(&w.closed, 0, 1) {
			return nil
		}

		// 先关闭消息通道，确保不会有新消息写入
		close(w.msgChan)

		// 等待所有消息处理完成
		for len(w.msgChan) > 0 {
			time.Sleep(time.Millisecond * 10)
		}

		// 最后发送关闭信号
		close(w.done)
	}

	w.mu.Lock()
	defer w.mu.Unlock()

	if w.outputFileHle != nil {
		if err := w.outputFileHle.Sync(); err != nil {
			return err
		}
		if err := w.outputFileHle.Close(); err != nil {
			return err
		}
		w.outputFileHle = nil
	}

	// 清理其他资源
	w.logger = nil
	w.contentParser = nil
	return nil
}

// ensureOpenFile 确保文件打开
func (w *FileWriter) ensureOpenFile() error {
	now := time.Now()
	w.ensureOutputFileName(now)
	w.rotate.lastRotate = now

	// 确保文件存在
	if err := w.ensureFileExists(w.outputFileName); err != nil {
		return fmt.Errorf("打开日志文件失败: %w", err)
	}
	return nil
}

// ensureFileExists 确保文件存在, 如果文件不存在则创建
func (w *FileWriter) ensureFileExists(fileName string) error {
	if w.outputFileHle == nil {
		w.outputFileName = fileName
		if err := w.initNewFile(); err != nil {
			return err
		}
	}
	return nil
}

// ensureOutputFileName 确保输出文件名
func (w *FileWriter) ensureOutputFileName(t time.Time) {
	if w.baseFileName == "" {
		if w.rotate.Enabled {
			switch w.rotate.Mode {
			case RotateYearly:
				w.baseFileName = "app-{Timestamp:yyyy}.log"
			case RotateMonthly:
				w.baseFileName = "app-{Timestamp:yyyyMM}.log"
			case RotateWeekly:
				w.baseFileName = "app-{Timestamp:yyyyww}.log"
			case RotateDaily:
				w.baseFileName = "app-{Timestamp:yyyyMMdd}.log"
			case RotateHourly:
				w.baseFileName = "app-{Timestamp:yyyyMMddHH}.log"
			default:
				w.baseFileName = "app.log"
			}
		} else {
			w.baseFileName = "app.log"
		}
		w.outputFileName = w.baseFileName
	}

	// 重置解析器
	w.fileParser.Reset()

	// 设置时间属性
	w.fileParser.WithProperty("Timestamp", t)

	// 解析文件名
	w.outputFileName = w.fileParser.Parse()
}

// initNewFile 初始化新的日志文件
func (w *FileWriter) initNewFile() error {
	// 确保目录存在
	if err := os.MkdirAll(w.baseFilePath, 0755); err != nil {
		return fmt.Errorf("创建日志目录失败: %w", err)
	}

	// 打开文件,如果不存在则创建
	filePath := filepath.Join(w.baseFilePath, w.outputFileName)
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return fmt.Errorf("打开日志文件失败: %w", err)
	}

	// 获取文件信息
	fileInfo, err := file.Stat()
	if err != nil {
		file.Close()
		return fmt.Errorf("获取文件信息失败: %w", err)
	}

	// 更新文件句柄和大小
	w.outputFileHle = file
	w.fileSize = fileInfo.Size()
	w.baseFileName = w.outputFileName

	return nil
}

// checkTimeRotation 检查是否需要按时间轮转
func (w *FileWriter) checkTimeRotation(now time.Time) bool {
	switch w.rotate.Mode {
	case RotateYearly:
		return now.Year() != w.rotate.lastRotate.Year()
	case RotateMonthly:
		return now.Year() != w.rotate.lastRotate.Year() ||
			now.Month() != w.rotate.lastRotate.Month()
	case RotateWeekly:
		y1, w1 := now.ISOWeek()
		y2, w2 := w.rotate.lastRotate.ISOWeek()
		return y1 != y2 || w1 != w2
	case RotateDaily:
		return now.Year() != w.rotate.lastRotate.Year() ||
			now.Month() != w.rotate.lastRotate.Month() ||
			now.Day() != w.rotate.lastRotate.Day()
	case RotateHourly:
		return now.Year() != w.rotate.lastRotate.Year() ||
			now.Month() != w.rotate.lastRotate.Month() ||
			now.Day() != w.rotate.lastRotate.Day() ||
			now.Hour() != w.rotate.lastRotate.Hour()
	default:
		return false
	}
}

// openFile 打开文件
func (w *FileWriter) openFile(filename string) error {
	filepath := filepath.Join(w.baseFilePath, filename)

	f, err := os.OpenFile(filepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("创建新日志文件失败: %w", err)
	}

	// 获取新文件的当前大小
	if info, err := f.Stat(); err == nil {
		w.fileSize = info.Size()
	} else {
		w.fileSize = 0
	}

	w.outputFileHle = f
	w.logger = log.New(f, "", log.Ldate|log.Ltime|log.Lmicroseconds)

	return nil
}

func (w *FileWriter) getOutputPath() string {
	return filepath.Join(w.baseFilePath, w.outputFileName)
}

// cleanExpiredFiles 清理过期的日志文件
func (w *FileWriter) cleanExpiredFiles() error {
	if w.rotate.MaxAge <= 0 {
		return nil
	}

	dir := w.baseFilePath
	files, err := os.ReadDir(dir)
	if err != nil {
		return fmt.Errorf("读取日志目录失败: %w", err)
	}

	now := time.Now()
	expireTime := now.Add(-time.Duration(w.rotate.MaxAge) * time.Hour * 24)

	for _, file := range files {
		// 跳过目录
		if file.IsDir() {
			continue
		}

		info, err := file.Info()
		if err != nil {
			continue
		}

		// 如果文件修改时间早于过期时间,则删除
		if info.ModTime().Before(expireTime) {
			filePath := filepath.Join(dir, info.Name())
			if err := os.Remove(filePath); err != nil {
				return fmt.Errorf("删除过期日志文件失败: %w", err)
			}
		}
	}

	return nil
}

func getGoroutineID() int64 {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, _ := strconv.ParseInt(idField, 10, 64)
	return id
}
